package com.sr.security;

import com.sr.constant.Constant;
import com.sr.security.filter.JwtTokenAuthenticationFilter;
import com.sr.security.filter.LoginUserFilter;
import com.sr.security.handler.AccessAuthenticationDeniedHandler;
import com.sr.security.handler.AccessAuthenticationEntryPoint;
import com.sr.security.handler.LogoutHandler;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

/**
 * @author xiaochi
 * @date 2022/5/2 16:51
 * @desc SecurityConfig
 */
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor(onConstructor = @__)
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Value("${filter.loginUrl}")
    private String loginUrl;
    @Value("${filter.logoutUrl}")
    private String logoutUrl;
    @Value("${filter.excludesUrl}")
    private String[] excludesUrl;

    private final UserDetailService userDetailService;

    private String[] excludes(){
        String[] excludes = {
            "/**/*.css","/**/*.js","/favicon.ico",
            "/druid/**","/webjars/**","/v2/api-docs","/swagger/**","/swagger-resources/**","/swagger-ui.html",
        };
        String[] excludesPath = new String[excludesUrl.length + excludes.length];
        System.arraycopy(excludesUrl,0,excludesPath,0,excludesUrl.length);
        System.arraycopy(excludes,0,excludesPath,excludesUrl.length,excludes.length);
        return excludesPath;
    }

    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder(){
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(new JwtAuthenticationProvider(userDetailService,bCryptPasswordEncoder()))
            .userDetailsService(userDetailService)
            .passwordEncoder(bCryptPasswordEncoder());
    }

    /**
     * 创建登录过滤器
     * @return
     * @throws Exception
     */
    @Bean
    public LoginUserFilter loginUserFilter() throws Exception {
        LoginUserFilter loginUserFilter = new LoginUserFilter(loginUrl);
        loginUserFilter.setAuthenticationManager(authenticationManagerBean());
        return loginUserFilter;
    }

    /**
     * 创建验证过滤器
     * @return
     * @throws Exception
     */
    @Bean
    public JwtTokenAuthenticationFilter jwtTokenAuthenticationFilter() throws Exception {
        JwtTokenAuthenticationFilter authenticationFilter = new JwtTokenAuthenticationFilter();
        authenticationFilter.setExcludes(excludes());
        authenticationFilter.setUserFilter(loginUserFilter());
        return authenticationFilter;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors().and().csrf().disable();
        http.headers().frameOptions().disable().cacheControl().disable();
        http.addFilterBefore(jwtTokenAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
                .addFilterBefore(loginUserFilter(),JwtTokenAuthenticationFilter.class)
                .formLogin()
                .loginProcessingUrl(loginUrl)
                .and()
                .logout().logoutUrl(logoutUrl).logoutSuccessHandler(new LogoutHandler(loginUserFilter()))
                .and()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                .antMatchers(HttpMethod.OPTIONS,"/**").permitAll()
                .antMatchers(excludes()).permitAll()
                .antMatchers(Constant.Api.BACK_PREFIX +"/**").authenticated()// 只拦截后台接口
                //.anyRequest().authenticated() // 不拦截所有，只拦截后台接口
                .and()
                .exceptionHandling()
                .accessDeniedHandler(new AccessAuthenticationDeniedHandler(loginUserFilter()))
                .authenticationEntryPoint(new AccessAuthenticationEntryPoint(loginUserFilter()));
    }
}
